//
//  XMLHandlerTest.swift
//  AlphaWalletTests
//
//  Created by James Sangalli on 11/4/18.
//

import Foundation
import XCTest
@testable import AlphaWallet
import BigInt
import AlphaWalletAddress
import AlphaWalletCore
import AlphaWalletFoundation
@testable import AlphaWalletTokenScript

// swiftlint:disable type_body_length
class XMLHandlerTest: XCTestCase {
    let tokenHex = "0x00000000000000000000000000000000fefe5ae99a3000000000000000010001".substring(from: 2)

    func testParser() {
        let assetDefinitionStore = AssetDefinitionStore.make()
        let token = assetDefinitionStore.xmlHandler(forContract: AlphaWalletFoundation.Constants.nullAddress, tokenType: .erc20).getToken(
                name: "",
                symbol: "",
                fromTokenIdOrEvent: .tokenId(tokenId: BigUInt(tokenHex, radix: 16)!),
                index: UInt16(1),
                inWallet: .make(),
                server: .main,
                tokenType: TokenType.erc875
        )
        XCTAssertNotNil(token)
    }

    //TODO fix test
//    func testHasAssetDefinition() {
//        let store = AssetDefinitionStore(backingStore: AssetDefinitionInMemoryBackingStore())
//        let address1 = AlphaWallet.Address.ethereumAddress(eip55String: "0x0000000000000000000000000000000000000001")
//        let address2 = AlphaWallet.Address.ethereumAddress(eip55String: "0x0000000000000000000000000000000000000002")
//        store[address1] = ""
//        XCTAssertTrue(XMLHandler(contract: address1.eip55String, assetDefinitionStore: store).hasAssetDefinition)
//        XCTAssertFalse(XMLHandler(contract: address2.eip55String, assetDefinitionStore: store).hasAssetDefinition)
//    }

// swiftlint:disable function_body_length
    func testExtractingAttributesWithNamespaceInXML() {
        // swiftlint:disable line_length
        let xml = """
            <ts:token xmlns:ethereum="urn:ethereum:constantinople" xmlns:ts="http://tokenscript.org/2020/06/tokenscript" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" custodian="false" xsi:schemaLocation="http://tokenscript.org/2020/06/tokenscript http://tokenscript.org/2020/06/tokenscript.xsd">
                <ts:label>
                    <ts:plurals xml:lang="en">
                        <ts:string quantity="one">Ticket</ts:string>
                        <ts:string quantity="other">Tickets</ts:string>
                    </ts:plurals>
                    <ts:plurals xml:lang="es">
                        <ts:string quantity="one">Boleto de admisión</ts:string>
                        <ts:string quantity="other">Boleto de admisiónes</ts:string>
                    </ts:plurals>
                    <ts:plurals xml:lang="zh">
                        <ts:string quantity="one">入場券</ts:string>
                        <ts:string quantity="other">入場券</ts:string>
                    </ts:plurals>
                </ts:label>

                <ts:contract interface="erc875" name="FIFA">
                    <ts:address network="1">0xA66A3F08068174e8F005112A8b2c7A507a822335</ts:address>
                </ts:contract>

                <ts:origins>
                    <!-- Define the contract which holds the token that the user will use -->
                    <ts:ethereum contract="FIFA"></ts:ethereum>
                </ts:origins>

                <ts:cards>
                    <ts:card type="token">
                        <ts:item-view xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
                            <xhtml:style type="text/css">.tbml-count {
              font-family: "SourceSansPro";
              font-weight: bolder;
              font-size: x-large;
              color: white;
            }
            .tbml-country{
              font-family: "SourceSansPro";
              font-weight: bolder;
              font-size: x-large;
              color: white;
            }
            .tbml-date {
              font-family: "SourceSansPro";
              font-size: small;
              color: white;
            }
            .tbml-time {
              font-family: "SourceSansPro";
              font-size: small;
              color: white;
            }
            .tbml-venue {
              font-family: "SourceSansPro";
              font-size: small;
              color: white;
            }
            .tbml-category {
              font-family: "SourceSansPro";
              font-size: small;
              color: white;
            }

            .country_container{
              padding-bottom: 1.6em;
            }
            .datetime_container{
              padding-bottom: 0.5em;
            }
            .venue_container{
              padding-bottom: 0.5em;
            }
            .category_container{
              padding-bottom: 0.5em;
            }

            html {
            }
            body {
              padding: 0px;
              margin: 0px;
            }
            div {
              margin: 0px;
              padding: 0px;
            }
            .data-icon {
              height:16px;
              vertical-align: middle
            }

            .ticket{
              background-image: url();
              border-radius: 15px;
              flex-direction: column;
              background-size: contain;
              width: 100%;
              background-repeat: no-repeat;
              height: 52.7vw;
              padding-left: auto;
              padding-right: auto;
              background-position: center;
              background-attachment: fixed;
            }

            .ticket_inner{
              flex-shrink: 1;
              padding-left: 10.5em;
              padding-top: 2.5em;
            }
            </xhtml:style>
                            <script type="text/javascript">//
            (function() {
                'use strict'

                function GeneralizedTime(generalizedTime) {
                    this.rawData = generalizedTime;
                }

                GeneralizedTime.prototype.getYear = function () {
                    return parseInt(this.rawData.substring(0, 4), 10);
                }

                GeneralizedTime.prototype.getMonth = function () {
                    return parseInt(this.rawData.substring(4, 6), 10) - 1;
                }

                GeneralizedTime.prototype.getDay = function () {
                    return parseInt(this.rawData.substring(6, 8), 10)
                },

                GeneralizedTime.prototype.getHours = function () {
                    return parseInt(this.rawData.substring(8, 10), 10)
                },

                GeneralizedTime.prototype.getMinutes = function () {
                    var minutes = parseInt(this.rawData.substring(10, 12), 10)
                    if (minutes) return minutes
                    return 0
                },

                GeneralizedTime.prototype.getSeconds = function () {
                    var seconds = parseInt(this.rawData.substring(12, 14), 10)
                    if (seconds) return seconds
                    return 0
                },

                GeneralizedTime.prototype.getMilliseconds = function () {
                    var startIdx
                    if (time.indexOf('.') !== -1) {
                        startIdx = this.rawData.indexOf('.') + 1
                    } else if (time.indexOf(',') !== -1) {
                        startIdx = this.rawData.indexOf(',') + 1
                    } else {
                        return 0
                    }

                    var stopIdx = time.length - 1
                    var fraction = '0' + '.' + time.substring(startIdx, stopIdx)
                    var ms = parseFloat(fraction) * 1000
                    return ms
                },

                GeneralizedTime.prototype.getTimeZone = function () {
                    let time = this.rawData;
                    var length = time.length
                    var symbolIdx
                    if (time.charAt(length - 1 ) === 'Z') return 0
                    if (time.indexOf('+') !== -1) {
                        symbolIdx = time.indexOf('+')
                    } else if (time.indexOf('-') !== -1) {
                        symbolIdx = time.indexOf('-')
                    } else {
                        return NaN
                    }

                    var minutes = time.substring(symbolIdx + 2)
                    var hours = time.substring(symbolIdx + 1, symbolIdx + 2)
                    var one = (time.charAt(symbolIdx) === '+') ? 1 : -1

                    var intHr = one * parseInt(hours, 10) * 60 * 60 * 1000
                    var intMin = one * parseInt(minutes, 10) * 60 * 1000
                    var ms = minutes ? intHr + intMin : intHr
                    return ms
                }

                if (typeof exports === 'object') {
                    module.exports = GeneralizedTime
                } else if (typeof define === 'function' &amp;amp;&amp;amp; define.amd) {
                    define(GeneralizedTime)
                } else {
                    window.GeneralizedTime = GeneralizedTime
                }
            }())

            class Token {
                constructor(tokenInstance) {
                    this.props = tokenInstance
                }

                formatGeneralizedTimeToDate(str) {
                    const d = new GeneralizedTime(str)
                    return new Date(d.getYear(), d.getMonth(), d.getDay(), d.getHours(), d.getMinutes(), d.getSeconds()).toLocaleDateString()
                }
                formatGeneralizedTimeToTime(str) {
                    const d = new GeneralizedTime(str)
                    return new Date(d.getYear(), d.getMonth(), d.getDay(), d.getHours(), d.getMinutes(), d.getSeconds()).toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})
                }

                render() {
                    let time;
                    let date;
                    if (this.props.time == null) {
                        time = ""
                        date = ""
                    } else {
                        time = this.formatGeneralizedTimeToTime(this.props.time.generalizedTime)
                        date = this.props.time == null ? "": this.formatGeneralizedTimeToDate(this.props.time.generalizedTime)
                    }
                    return `
                    &lt;div class="ticket"&gt;
                      &lt;div class="ticket_inner"&gt;

                        &lt;div class="country_container"&gt;
                          &lt;span class="tbml-country"&gt;${this.props._count}x ${this.props.countryA} vs ${this.props.countryB}&lt;/span&gt;
                        &lt;/div&gt;

                        &lt;div class="datetime_container"&gt;
                          &lt;span class="tbml-date"&gt;${date} | ${time}&lt;/span&gt;
                        &lt;/div&gt;

                        &lt;div class="venue_container"&gt;
                            &lt;span class="tbml-venue"&gt;${this.props.venue} | ${this.props.locality}&lt;/span&gt;
                        &lt;/div&gt;

                        &lt;div class="category_container"&gt;
                          &lt;span class="tbml-category"&gt;${this.props.category}, M${this.props.match}&lt;/span&gt;
                        &lt;/div&gt;

                      &lt;/div&gt;
                    &lt;/div&gt;
                    `;
                }
            }

            web3.tokens.dataChanged = (oldTokens, updatedTokens, tokenCardId) =&gt; {
                const currentTokenInstance = updatedTokens.currentInstance;
                document.getElementById(tokenCardId).innerHTML = new Token(currentTokenInstance).render();
            };
            //
            </script>
                        </ts:item-view>
                        <ts:view xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
                            <xhtml:style type="text/css">.tbml-count {
              font-family: "SourceSansPro";
              font-weight: bolder;
              font-size: x-large;
              color: white;
            }
            .tbml-country{
              font-family: "SourceSansPro";
              font-weight: bolder;
              font-size: x-large;
              color: white;
            }
            .tbml-date {
              font-family: "SourceSansPro";
              font-size: small;
              color: white;
            }
            .tbml-time {
              font-family: "SourceSansPro";
              font-size: small;
              color: white;
            }
            .tbml-venue {
              font-family: "SourceSansPro";
              font-size: small;
              color: white;
            }
            .tbml-category {
              font-family: "SourceSansPro";
              font-size: small;
              color: white;
            }

            .country_container{
              padding-bottom: 1.6em;
            }
            .datetime_container{
              padding-bottom: 0.5em;
            }
            .venue_container{
              padding-bottom: 0.5em;
            }
            .category_container{
              padding-bottom: 0.5em;
            }

            html {
            }
            body {
              padding: 0px;
              margin: 0px;
            }
            div {
              margin: 0px;
              padding: 0px;
            }
            .data-icon {
              height:16px;
              vertical-align: middle
            }

            .ticket{
              background-image: url();
              border-radius: 15px;
              flex-direction: column;
              background-size: contain;
              width: 100%;
              background-repeat: no-repeat;
              height: 52.7vw;
              padding-left: auto;
              padding-right: auto;
              background-position: center;
              background-attachment: fixed;
            }

            .ticket_inner{
              flex-shrink: 1;
              padding-left: 10.5em;
              padding-top: 2.5em;
            }
            </xhtml:style>
                            <script type="text/javascript">//
            (function() {
                'use strict'

                function GeneralizedTime(generalizedTime) {
                    this.rawData = generalizedTime;
                }

                GeneralizedTime.prototype.getYear = function () {
                    return parseInt(this.rawData.substring(0, 4), 10);
                }

                GeneralizedTime.prototype.getMonth = function () {
                    return parseInt(this.rawData.substring(4, 6), 10) - 1;
                }

                GeneralizedTime.prototype.getDay = function () {
                    return parseInt(this.rawData.substring(6, 8), 10)
                },

                GeneralizedTime.prototype.getHours = function () {
                    return parseInt(this.rawData.substring(8, 10), 10)
                },

                GeneralizedTime.prototype.getMinutes = function () {
                    var minutes = parseInt(this.rawData.substring(10, 12), 10)
                    if (minutes) return minutes
                    return 0
                },

                GeneralizedTime.prototype.getSeconds = function () {
                    var seconds = parseInt(this.rawData.substring(12, 14), 10)
                    if (seconds) return seconds
                    return 0
                },

                GeneralizedTime.prototype.getMilliseconds = function () {
                    var startIdx
                    if (time.indexOf('.') !== -1) {
                        startIdx = this.rawData.indexOf('.') + 1
                    } else if (time.indexOf(',') !== -1) {
                        startIdx = this.rawData.indexOf(',') + 1
                    } else {
                        return 0
                    }

                    var stopIdx = time.length - 1
                    var fraction = '0' + '.' + time.substring(startIdx, stopIdx)
                    var ms = parseFloat(fraction) * 1000
                    return ms
                },

                GeneralizedTime.prototype.getTimeZone = function () {
                    let time = this.rawData;
                    var length = time.length
                    var symbolIdx
                    if (time.charAt(length - 1 ) === 'Z') return 0
                    if (time.indexOf('+') !== -1) {
                        symbolIdx = time.indexOf('+')
                    } else if (time.indexOf('-') !== -1) {
                        symbolIdx = time.indexOf('-')
                    } else {
                        return NaN
                    }

                    var minutes = time.substring(symbolIdx + 2)
                    var hours = time.substring(symbolIdx + 1, symbolIdx + 2)
                    var one = (time.charAt(symbolIdx) === '+') ? 1 : -1

                    var intHr = one * parseInt(hours, 10) * 60 * 60 * 1000
                    var intMin = one * parseInt(minutes, 10) * 60 * 1000
                    var ms = minutes ? intHr + intMin : intHr
                    return ms
                }

                if (typeof exports === 'object') {
                    module.exports = GeneralizedTime
                } else if (typeof define === 'function' &amp;amp;&amp;amp; define.amd) {
                    define(GeneralizedTime)
                } else {
                    window.GeneralizedTime = GeneralizedTime
                }
            }())

            class Token {
                constructor(tokenInstance) {
                    this.props = tokenInstance
                }

                formatGeneralizedTimeToDate(str) {
                    const d = new GeneralizedTime(str)
                    return new Date(d.getYear(), d.getMonth(), d.getDay(), d.getHours(), d.getMinutes(), d.getSeconds()).toLocaleDateString()
                }
                formatGeneralizedTimeToTime(str) {
                    const d = new GeneralizedTime(str)
                    return new Date(d.getYear(), d.getMonth(), d.getDay(), d.getHours(), d.getMinutes(), d.getSeconds()).toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})
                }

                render() {
                    let time;
                    let date;
                    if (this.props.time == null) {
                        time = ""
                        date = ""
                    } else {
                        time = this.formatGeneralizedTimeToTime(this.props.time.generalizedTime)
                        date = this.props.time == null ? "": this.formatGeneralizedTimeToDate(this.props.time.generalizedTime)
                    }
                    return `
                    &lt;div class="ticket"&gt;
                      &lt;div class="ticket_inner"&gt;

                        &lt;div class="country_container"&gt;
                          &lt;span class="tbml-country"&gt;${this.props._count}x ${this.props.countryA} vs ${this.props.countryB}&lt;/span&gt;
                        &lt;/div&gt;

                        &lt;div class="datetime_container"&gt;
                          &lt;span class="tbml-date"&gt;${date} | ${time}&lt;/span&gt;
                        &lt;/div&gt;

                        &lt;div class="venue_container"&gt;
                            &lt;span class="tbml-venue"&gt;${this.props.venue} | ${this.props.locality}&lt;/span&gt;
                        &lt;/div&gt;

                        &lt;div class="category_container"&gt;
                          &lt;span class="tbml-category"&gt;${this.props.category}, M${this.props.match}&lt;/span&gt;
                        &lt;/div&gt;

                      &lt;/div&gt;
                    &lt;/div&gt;
                    `;
                }
            }

            web3.tokens.dataChanged = (oldTokens, updatedTokens, tokenCardId) =&gt; {
                const currentTokenInstance = updatedTokens.currentInstance;
                document.getElementById(tokenCardId).innerHTML = new Token(currentTokenInstance).render();
            };
            //
            </script>
                        </ts:view>
                    </ts:card>
                </ts:cards>

                <ts:ordering>
                    <ts:order bitmask="FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" name="default">
                        <ts:byName field="locality"></ts:byName>
                        <ts:byValue field="match"></ts:byValue>
                        <ts:byValue field="number"></ts:byValue>
                    </ts:order>
                    <ts:order bitmask="FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" name="concise">
                        <ts:byValue field="match"></ts:byValue>
                        <ts:byValue field="category"></ts:byValue>
                        <ts:byValue field="number"></ts:byValue>
                    </ts:order>
                </ts:ordering>

                    <!--
                There are 64 matches (1-64), each has up to 16 ticket classes,
                within each class, there are less than 65536 tickets.  A compact
                format identifier would consist of 26 bits:

                [6] [4] [16]

                Representing:

                    Match ID: 1-64
                    Class: 1-16
                    Seats: 0-65536

                These are represented by 7 hex codes. Therefore 0x40F0481 means
                the final match (64th), class F (highest) ticket number 1153. But
                we didn't end up using this compatct form.

                Information about a match, like Venue, City, Date, which team
                against which, can all be looked up by MatchID. There are
                advantages and disadvantages in encoding them by a look up table
                or by a bit field.

                The advantage of storing them as a bit field is that one can
                enquiry the range of it in the market queue server without the
                server kowing the meaning of the bitfields. Furthermore it make
                the android and ios library which accesses the XML file a bit
                easier to write, but at the cost that the ticket issuing
                (authorisation) server is a bit more complicated.

                For now we decide to use bit-fields.  The fields, together with
                its bitwidth or byte-width are represented in this table:

                Fields:           City,   Venue,  Date,   TeamA,  TeamB,  Match, Category
                Maximum, 2018:    11,     12,     32,     32,     32,     64,    16
                Maximum, all time:64,     64,     64,     32,     32,     64,    64
                Bitwidth:         6,      6,      6,      5,      5,      6,     6
                Bytewidth:        1,      1,      4,      3,      3,      1,     1,

                In practise, because this XML file is used in 3 to 4 places
                (authorisation server, ios, android, potentially market-queue),
                Weiwu thought that it helps the developers if we use byte-fields
                instead of bit-fields.
                1.3.6.1.4.1.1466.115.121.1.15 is DirectoryString
                1.3.6.1.4.1.1466.115.121.1.24 is GeneralisedTime
                1.3.6.1.4.1.1466.115.121.1.27 is Integer
              -->
                    <ts:attribute name="locality" oid="2.5.4.7">
                        <ts:type><ts:syntax>1.3.6.1.4.1.1466.115.121.1.15</ts:syntax></ts:type>
                        <ts:label>
                            <ts:string xml:lang="en">City</ts:string>
                            <ts:string xml:lang="zh">城市</ts:string>
                            <ts:string xml:lang="es">Ciudad</ts:string>
                            <ts:string xml:lang="ru">город</ts:string>
                        </ts:label>
                        <ts:origins>
                            <ts:token-id as="uint" bitmask="00000000000000000000000000000000FF000000000000000000000000000000">
                                <ts:mapping>
                                    <ts:option key="1">
                                        <ts:value xml:lang="ru">Москва́</ts:value>
                                        <ts:value xml:lang="en">Moscow</ts:value>
                                        <ts:value xml:lang="zh">莫斯科</ts:value>
                                        <ts:value xml:lang="es">Moscú</ts:value>
                                    </ts:option>
                                    <ts:option key="2">
                                        <ts:value xml:lang="ru">Санкт-Петербу́рг</ts:value>
                                        <ts:value xml:lang="en">Saint Petersburg</ts:value>
                                        <ts:value xml:lang="zh">圣彼得堡</ts:value>
                                        <ts:value xml:lang="es">San Petersburgo</ts:value>
                                    </ts:option>
                                    <ts:option key="3">
                                        <ts:value xml:lang="ru">сочи</ts:value>
                                        <ts:value xml:lang="en">Sochi</ts:value>
                                        <ts:value xml:lang="zh">索契</ts:value>
                                        <ts:value xml:lang="es">Sochi</ts:value>
                                    </ts:option>
                                    <ts:option key="4">
                                        <ts:value xml:lang="ru">екатеринбург</ts:value>
                                        <ts:value xml:lang="en">Ekaterinburg</ts:value>
                                        <ts:value xml:lang="zh">叶卡捷琳堡</ts:value>
                                        <ts:value xml:lang="es">Ekaterinburg</ts:value>
                                    </ts:option>
                                    <ts:option key="5">
                                        <ts:value xml:lang="ru">Саранск</ts:value>
                                        <ts:value xml:lang="en">Saransk</ts:value>
                                        <ts:value xml:lang="zh">萨兰斯克</ts:value>
                                        <ts:value xml:lang="es">Saransk</ts:value>
                                    </ts:option>
                                    <ts:option key="6">
                                        <ts:value xml:lang="ru">казань</ts:value>
                                        <ts:value xml:lang="en">Kazan</ts:value>
                                        <ts:value xml:lang="zh">喀山</ts:value>
                                        <ts:value xml:lang="es">Kazan</ts:value>
                                    </ts:option>
                                    <ts:option key="7">
                                        <ts:value xml:lang="ru">Нижний Новгород</ts:value>
                                        <ts:value xml:lang="en">Nizhny Novgorod</ts:value>
                                        <ts:value xml:lang="zh">下诺夫哥罗德</ts:value>
                                        <ts:value xml:lang="es">Nizhny Novgorod</ts:value>
                                    </ts:option>
                                    <ts:option key="8">
                                        <ts:value xml:lang="ru">Ростов-на-Дону</ts:value>
                                        <ts:value xml:lang="en">Rostov-on-Don</ts:value>
                                        <ts:value xml:lang="zh">顿河畔罗斯托夫</ts:value>
                                        <ts:value xml:lang="es">Rostov-on-Don</ts:value>
                                    </ts:option>
                                    <ts:option key="9">
                                        <ts:value xml:lang="ru">Самара</ts:value>
                                        <ts:value xml:lang="en">Samara</ts:value>
                                        <ts:value xml:lang="zh">萨马拉</ts:value>
                                        <ts:value xml:lang="es">Samara</ts:value>
                                    </ts:option>
                                    <ts:option key="10">
                                        <ts:value xml:lang="ru">Волгоград</ts:value>
                                        <ts:value xml:lang="en">Volgograd</ts:value>
                                        <ts:value xml:lang="zh">伏尔加格勒</ts:value>
                                        <ts:value xml:lang="es">Volgogrado</ts:value>
                                    </ts:option>
                                    <ts:option key="11">
                                        <ts:value xml:lang="ru">Калининград</ts:value>
                                        <ts:value xml:lang="en">Kaliningrad</ts:value>
                                        <ts:value xml:lang="zh">加里宁格勒</ts:value>
                                        <ts:value xml:lang="es">Kaliningrad</ts:value>
                                    </ts:option>
                                </ts:mapping>
                            </ts:token-id>
                        </ts:origins>
                    </ts:attribute>

                    <ts:attribute name="venue">
                        <ts:type><ts:syntax>1.3.6.1.4.1.1466.115.121.1.15</ts:syntax></ts:type>
                        <ts:label>
                            <ts:string xml:lang="en">Venue</ts:string>
                            <ts:string xml:lang="zh">场馆</ts:string>
                            <ts:string xml:lang="es">Lugar</ts:string>
                            <ts:string xml:lang="ru">место встречи</ts:string>
                        </ts:label>
                        <ts:origins>
                            <ts:token-id as="uint" bitmask="0000000000000000000000000000000000FF0000000000000000000000000000">
                                <ts:mapping>
                                    <ts:option key="1">
                                        <ts:value xml:lang="ru">Стадион Калининград</ts:value>
                                        <ts:value xml:lang="en">Kaliningrad Stadium</ts:value>
                                        <ts:value xml:lang="zh">加里宁格勒体育场</ts:value>
                                        <ts:value xml:lang="es">Estadio de Kaliningrado</ts:value>
                                    </ts:option>
                                    <ts:option key="2">
                                        <ts:value xml:lang="ru">Екатеринбург Арена</ts:value>
                                        <ts:value xml:lang="en">Volgograd Arena</ts:value>
                                        <ts:value xml:lang="zh">伏尔加格勒体育场</ts:value>
                                        <ts:value xml:lang="es">Volgogrado Arena</ts:value>
                                    </ts:option>
                                    <ts:option key="3">
                                        <ts:value xml:lang="ru">Казань Арена</ts:value>
                                        <ts:value xml:lang="en">Ekaterinburg Arena</ts:value>
                                        <ts:value xml:lang="zh">加里宁格勒体育场</ts:value>
                                        <ts:value xml:lang="es">Ekaterimburgo Arena</ts:value>
                                    </ts:option>
                                    <ts:option key="4">
                                        <ts:value xml:lang="ru">Мордовия Арена</ts:value>
                                        <ts:value xml:lang="en">Fisht Stadium</ts:value>
                                        <ts:value xml:lang="zh">费什体育场</ts:value>
                                        <ts:value xml:lang="es">Estadio Fisht</ts:value>
                                    </ts:option>
                                    <ts:option key="5">
                                        <ts:value xml:lang="ru">Ростов Арена</ts:value>
                                        <ts:value xml:lang="en">Kazan Arena</ts:value>
                                        <ts:value xml:lang="zh">喀山体育场</ts:value>
                                        <ts:value xml:lang="es">Kazan Arena</ts:value>
                                    </ts:option>
                                    <ts:option key="6">
                                        <ts:value xml:lang="ru">Самара Арена</ts:value>
                                        <ts:value xml:lang="en">Nizhny Novgorod Stadium</ts:value>
                                        <ts:value xml:lang="zh">下诺夫哥罗德体育场</ts:value>
                                        <ts:value xml:lang="es">Estadio de Nizhni Novgorod</ts:value>
                                    </ts:option>
                                    <ts:option key="7">
                                        <ts:value xml:lang="ru">Стадион Калининград</ts:value>
                                        <ts:value xml:lang="en">Luzhniki Stadium</ts:value>
                                        <ts:value xml:lang="zh">卢日尼基体育场</ts:value>
                                        <ts:value xml:lang="es">Estadio Luzhniki</ts:value>
                                    </ts:option>
                                    <ts:option key="8">
                                        <ts:value xml:lang="ru">Стадион Лужники</ts:value>
                                        <ts:value xml:lang="en">Samara Arena</ts:value>
                                        <ts:value xml:lang="zh">萨马拉体育场</ts:value>
                                        <ts:value xml:lang="es">Samara Arena</ts:value>
                                    </ts:option>
                                    <ts:option key="9">
                                        <ts:value xml:lang="ru">Стадион Нижний Новгород</ts:value>
                                        <ts:value xml:lang="en">Rostov Arena</ts:value>
                                        <ts:value xml:lang="zh">罗斯托夫体育场</ts:value>
                                        <ts:value xml:lang="es">Rostov Arena</ts:value>
                                    </ts:option>
                                    <ts:option key="10">
                                        <ts:value xml:lang="ru">Стадион Спартак</ts:value>
                                        <ts:value xml:lang="en">Spartak Stadium</ts:value>
                                        <ts:value xml:lang="zh">斯巴达克体育场</ts:value>
                                        <ts:value xml:lang="es">Estadio del Spartak</ts:value>
                                    </ts:option>
                                    <ts:option key="11">
                                        <ts:value xml:lang="ru">Стадион Санкт-Петербург</ts:value>
                                        <ts:value xml:lang="en">Saint Petersburg Stadium</ts:value>
                                        <ts:value xml:lang="zh">圣彼得堡体育场</ts:value>
                                        <ts:value xml:lang="es">Estadio de San Petersburgo</ts:value>
                                    </ts:option>
                                    <ts:option key="12">
                                        <ts:value xml:lang="ru">Стадион Фишт</ts:value>
                                        <ts:value xml:lang="en">Mordovia Arena</ts:value>
                                        <ts:value xml:lang="zh">莫多维亚体育场</ts:value>
                                        <ts:value xml:lang="es">Mordovia Arena</ts:value>
                                    </ts:option>
                                </ts:mapping>
                            </ts:token-id>
                        </ts:origins>
                    </ts:attribute>

                    <ts:attribute name="countryA">
                        <ts:type><ts:syntax>1.3.6.1.4.1.1466.115.121.1.26</ts:syntax></ts:type>
                        <!-- Intentionally avoid using countryName
                     (SYNTAX 1.3.6.1.4.1.1466.115.121.1.11) per RFC 4519
                         CountryName is two-characters long, not 3-characters.
                     -->
                        <ts:label>
                            <ts:string xml:lang="en">Team A</ts:string>
                            <ts:string xml:lang="zh">甲队</ts:string>
                            <ts:string xml:lang="es">Equipo A</ts:string>
                        </ts:label>
                        <ts:origins>
                            <ts:token-id as="utf8" bitmask="00000000000000000000000000000000000000000000FFFFFF00000000000000"></ts:token-id>
                        </ts:origins>
                    </ts:attribute>

                    <ts:attribute name="countryB">
                        <ts:type><ts:syntax>1.3.6.1.4.1.1466.115.121.1.26</ts:syntax></ts:type>
                        <ts:label>
                            <ts:string xml:lang="en">Team B</ts:string>
                            <ts:string xml:lang="zh">乙队</ts:string>
                            <ts:string xml:lang="es">Equipo B</ts:string>
                        </ts:label>
                        <ts:origins>
                            <ts:token-id as="utf8" bitmask="00000000000000000000000000000000000000000000000000FFFFFF00000000"></ts:token-id>
                        </ts:origins>
                    </ts:attribute>

                    <ts:attribute name="match">
                        <ts:type><ts:syntax>1.3.6.1.4.1.1466.115.121.1.27</ts:syntax></ts:type>
                        <ts:label>
                            <ts:string xml:lang="en">Match</ts:string>
                            <ts:string xml:lang="zh">场次</ts:string>
                            <ts:string xml:lang="es">Evento</ts:string>
                        </ts:label>
                        <ts:origins>
                            <ts:token-id as="utf8" bitmask="00000000000000000000000000000000000000000000000000000000FF000000"></ts:token-id>
                        </ts:origins>
                    </ts:attribute>

                    <ts:attribute name="category">
                        <ts:type><ts:syntax>1.3.6.1.4.1.1466.115.121.1.15</ts:syntax></ts:type>
                        <ts:label>
                            <ts:string xml:lang="en">Cat</ts:string>
                            <ts:string xml:lang="zh">等级</ts:string>
                            <ts:string xml:lang="es">Cat</ts:string>
                        </ts:label>
                        <ts:origins>
                            <ts:token-id as="uint" bitmask="0000000000000000000000000000000000000000000000000000000000FF0000">
                                <ts:mapping>
                                    <ts:option key="1">
                                        <ts:value xml:lang="en">Category 1</ts:value>
                                        <ts:value xml:lang="zh">一类票</ts:value>
                                    </ts:option>
                                    <ts:option key="2">
                                        <ts:value xml:lang="en">Category 2</ts:value>
                                        <ts:value xml:lang="zh">二类票</ts:value>
                                    </ts:option>
                                    <ts:option key="3">
                                        <ts:value xml:lang="en">Category 3</ts:value>
                                        <ts:value xml:lang="zh">三类票</ts:value>
                                    </ts:option>
                                    <ts:option key="4">
                                        <ts:value xml:lang="en">Category 4</ts:value>
                                        <ts:value xml:lang="zh">四类票</ts:value>
                                    </ts:option>
                                    <ts:option key="5">
                                        <ts:value xml:lang="en">Match Club</ts:value>
                                        <ts:value xml:lang="zh">俱乐部坐席</ts:value>
                                    </ts:option>
                                    <ts:option key="6">
                                        <ts:value xml:lang="en">Match House Premier</ts:value>
                                        <ts:value xml:lang="zh">比赛之家坐席</ts:value>
                                    </ts:option>
                                    <ts:option key="7">
                                        <ts:value xml:lang="en">MATCH PAVILION</ts:value>
                                        <ts:value xml:lang="zh">款待大厅坐席</ts:value>
                                    </ts:option>
                                    <ts:option key="8">
                                        <ts:value xml:lang="en">MATCH BUSINESS SEAT</ts:value>
                                        <ts:value xml:lang="zh">商务坐席</ts:value>
                                    </ts:option>
                                    <ts:option key="9">
                                        <ts:value xml:lang="en">MATCH SHARED SUITE</ts:value>
                                        <ts:value xml:lang="zh">公共包厢</ts:value>
                                    </ts:option>
                                    <ts:option key="10">
                                        <ts:value xml:lang="en">TSARSKY LOUNGE</ts:value>
                                        <ts:value xml:lang="zh">特拉斯基豪华包厢</ts:value>
                                    </ts:option>
                                    <ts:option key="11">
                                        <ts:value xml:lang="en">MATCH PRIVATE SUITE</ts:value>
                                        <ts:value xml:lang="zh">私人包厢</ts:value>
                                    </ts:option>
                                </ts:mapping>
                            </ts:token-id>
                        </ts:origins>
                    </ts:attribute>
                    <ts:attribute name="time">
                        <ts:type><ts:syntax>1.3.6.1.4.1.1466.115.121.1.24</ts:syntax></ts:type>
                        <ts:label>
                            <ts:string xml:lang="en">Time</ts:string>
                            <ts:string xml:lang="zh">时间</ts:string>
                            <ts:string xml:lang="es">Tiempo</ts:string>
                            <ts:string xml:lang="ru">время</ts:string>
                        </ts:label>
                        <ts:origins>
                            <ts:token-id as="uint" bitmask="000000000000000000000000000000000000FFFFFFFF00000000000000000000">
                                <ts:mapping>
                                    <!-- $ TZ=Europe/Moscow date -d @1528988400 +%Y%m%d%H%M%S%z -->
                                    <ts:option key="1528988400">
                                        <ts:value>20180614180000+0300</ts:value>
                                    </ts:option>
                                    <!-- $ TZ=Europe/Moscow date -d @1529074800 +%Y%m%d%H%M%S%z -->
                                    <ts:option key="1529074800">
                                        <ts:value>20180615180000+0300</ts:value>
                                    </ts:option>
                                    <!-- $ TZ=Europe/Moscow date -d @1529420400 +%Y%m%d%H%M%S%z -->
                                    <ts:option key="1529420400">
                                        <ts:value>20180619180000+0300</ts:value>
                                    </ts:option>
                                    <!-- $ TZ=Europe/Moscow date -d @1529431200 +%Y%m%d%H%M%S%z -->
                                    <ts:option key="1529431200">
                                        <ts:value>20180619210000+0300</ts:value>
                                    </ts:option>
                                    <!-- $ TZ=Europe/Moscow date -d @1530900000 +%Y%m%d%H%M%S%z -->
                                    <ts:option key="1530900000">
                                        <ts:value>20180706210000+0300</ts:value>
                                    </ts:option>
                                    <!-- $ TZ=Europe/Moscow date -d @1531576800 +%Y%m%d%H%M%S%z -->
                                    <ts:option key="1531576800">
                                        <ts:value>20180714170000+0300</ts:value>
                                    </ts:option>
                                </ts:mapping>
                            </ts:token-id>
                        </ts:origins>
                    </ts:attribute>

                    <ts:attribute name="numero">
                        <ts:type><ts:syntax>1.3.6.1.4.1.1466.115.121.1.27</ts:syntax></ts:type>
                        <ts:label>
                            <ts:string>№</ts:string>
                        </ts:label>
                        <ts:origins>
                            <ts:token-id as="uint" bitmask="000000000000000000000000000000000000000000000000000000000000FFFF"></ts:token-id>
                        </ts:origins>
                    </ts:attribute>

            </ts:token>
        """
        let contractAddress = AlphaWallet.Address(string: "0xA66A3F08068174e8F005112A8b2c7A507a822335")!
        let store = AssetDefinitionStore(backingStore: AssetDefinitionInMemoryBackingStore(), networkService: FakeNetworkService(), blockchainsProvider: BlockchainsProviderImplementation.make(servers: [.main]), features: TokenScriptFeatures(), resetFolders: false)

        store.storeOfficialXmlForToken(contractAddress, xml: xml, fromUrl: URL(string: "http://google.com")!)
        let xmlHandler = store.xmlHandler(forContract: contractAddress, tokenType: .erc20)
        let tokenId = BigUInt("0000000000000000000000000000000002000000000000000000000000000000", radix: 16)!
        let server: RPCServer = .main
        let token = xmlHandler.getToken(name: "Some name", symbol: "Some symbol", fromTokenIdOrEvent: .tokenId(tokenId: tokenId), index: 1, inWallet: .make(), server: server, tokenType: TokenType.erc875)
        let values = token.values

        XCTAssertEqual(values["locality"]?.stringValue, "Saint Petersburg")
    }
// swiftlint:enable function_body_length

    func testNoAssetDefinition() {
        let store = AssetDefinitionStore(backingStore: AssetDefinitionInMemoryBackingStore(), networkService: FakeNetworkService(), blockchainsProvider: BlockchainsProviderImplementation.make(servers: [.main]), features: TokenScriptFeatures(), resetFolders: false)
        let xmlHandler = store.xmlHandler(forContract: AlphaWalletFoundation.Constants.nullAddress, tokenType: .erc875)
        let tokenId = BigUInt("0000000000000000000000000000000002000000000000000000000000000000", radix: 16)!
        let server: RPCServer = .main
        let token = xmlHandler.getToken(name: "Some name", symbol: "Some symbol", fromTokenIdOrEvent: .tokenId(tokenId: tokenId), index: 1, inWallet: .make(), server: server, tokenType: TokenType.erc721)
        let values = token.values
        XCTAssertTrue(values.isEmpty)
    }

    func testXPathNamePrefixing() {
        XCTAssertEqual("".addToXPath(namespacePrefix: "tb1:"), "")
        XCTAssertEqual("/part1/part2/part3".addToXPath(namespacePrefix: "tb1:"), "/tb1:part1/tb1:part2/tb1:part3")
        XCTAssertEqual("part1/part2/part3".addToXPath(namespacePrefix: "tb1:"), "tb1:part1/tb1:part2/tb1:part3")
    }
}
// swiftlint:enable type_body_length
